package cn.edu.cug.cs.gtl.plugins.redner4oi;
import cn.edu.cug.cs.gtl.apis.render.Selector;
import cn.edu.cug.cs.gtl.common.Pair;
import cn.edu.cug.cs.gtl.plugins.redner4oi.nodes.NodeImpl;
import cn.edu.cug.cs.gtl.plugins.redner4oi.nodes.SeparatorImpl;
import cn.edu.cug.cs.gtl.plugins.redner4oi.utils.RenderUtils;
import cn.edu.cug.cs.gtl.plugins.redner4oi.viewers.ViewerImpl;
import cn.edu.cug.cs.gtl.protos.*;
import cn.edu.cug.cs.gtl.apis.render.Render;
import cn.edu.cug.cs.gtl.apis.render.nodes.Node;
import cn.edu.cug.cs.gtl.apis.render.viewers.Viewer;
import com.openinventor.inventor.SoDB;
import com.openinventor.inventor.SoInput;
import com.openinventor.inventor.nodes.SoGroup;
import com.openinventor.inventor.nodes.*;
import com.openinventor.inventor.viewercomponents.awt.glcanvas.viewers.ViewerExaminer;
import cn.edu.cug.cs.gtl.api.wrapper.ProjectWrapper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


public class RenderImpl implements Render {

    private Project project=null;
    private SoNode sceneGraph = null;
    private ViewerExaminer viewer=null;

    //存放删除的结点，方便恢复时候使用
    private List<SoNode> soNodeList = new ArrayList<>();



    public RenderImpl() {
        viewer = new ViewerExaminer();
        sceneGraph = new SoSeparator();
        project =Project.newBuilder().build();
    }


    @Override
    public Selector getSelector() {
        return null;
    }

    @Override
    public void setProject(Project project) {
        this.project = project;
    }

    @Override
    public Project getProject() {
        return this.project;
    }

    @Override
    public Node getSceneGraph() {
        return new NodeImpl(sceneGraph);
    }

    @Override
    public void setSceneGraph(Node sceneGraph) {
        setNativeSceneGraph((SoNode) sceneGraph.getNativeObject());
    }

    @Override
    public Node loadFile(String fileName) throws IOException {
        if(fileName.endsWith("h3d")){
            project = ProjectWrapper.readH3DFile(fileName);
            return createProjectNode(project);
        }
        else{
            SoInput input = new SoInput();

            if ( !input.openFile(fileName) )
            {
                System.err.println("Cannot open file " + fileName);
                return null;
            }

            SoSeparator node = SoDB.readAll(input);
            if ( node == null )
            {
                System.err.println("Problem reading file");
                input.closeFile();
                return null;
            }

            input.closeFile();
            return new SeparatorImpl(node);
        }
    }

    @Override
    public Viewer getViewer() {
        return new  ViewerImpl(this.viewer);
    }


    @Override
    public void setViewer(Viewer viewer) {
        setNativeViewer((ViewerExaminer) viewer.getNativeObject());
    }


    @Override
    public void removeMapNode(Map map){
        SoNode soNode = RenderUtils.removeNode(getNativeSceneGraph(),map);
        if(soNode!=null)
            soNodeList.add(soNode);
    }

    @Override
    public void removeLayerNode(Layer layer){
        SoNode soNode = RenderUtils.removeNode(getNativeSceneGraph(),layer);
        if(soNode!=null)
            soNodeList.add(soNode);
    }


    @Override
    public void setVisible(Project project , boolean visible){
        Pair<SoNode,SoNode> p = RenderUtils.findNode(sceneGraph, project);
        SoGroup separator = (SoGroup) p.getFirst();
        if(separator!=null){
            for(int i=1;i<separator.getNumChildren();++i) {
                setProjectNodeVisible((SoGroup)separator.getChild(i),visible);
            }
        }
    }

    @Override
    public void setVisible(Map map , boolean visible){
        Pair<SoNode,SoNode> p = RenderUtils.findNode(sceneGraph, map);
        SoGroup separator = (SoGroup) p.getFirst();
        if(separator!=null){
            for(int i=1;i<separator.getNumChildren();++i) {
                setLayerNodeVisible((SoGroup)separator.getChild(i),visible);
            }
        }
    }

    @Override
    public void setVisible(Layer layer , boolean visible){
        Pair<SoNode,SoNode> p = RenderUtils.findNode(sceneGraph, layer);
        SoGroup separator = (SoGroup) p.getFirst();
        setLayerNodeVisible(separator,visible);
    }

    @Override
    public Pair<Node, Node> findNode(Project project) {
        if(project!=null){
            Pair<SoNode,SoNode> p = RenderUtils.findNode(sceneGraph, project);
            return new Pair<>(new NodeImpl(p.getFirst()),new NodeImpl(p.getSecond()));
        }
        return null;
    }

    @Override
    public Pair<Node, Node> findNode(Map map) {
        if(map!=null){
            Pair<SoNode,SoNode> p = RenderUtils.findNode(sceneGraph, map);
            return new Pair<>(new NodeImpl(p.getFirst()),new NodeImpl(p.getSecond()));
        }
        return null;
    }

    @Override
    public Pair<Node, Node> findNode(Layer layer) {
        if(layer!=null){
            Pair<SoNode,SoNode> p = RenderUtils.findNode(sceneGraph, layer);
            return new Pair<>(new NodeImpl(p.getFirst()),new NodeImpl(p.getSecond()));
        }
        return null;
    }

    @Override
    public Node removeNode(Project project) {
        if(project!=null){
            SoNode soNode = RenderUtils.removeNode(getNativeSceneGraph(),project);
            if(soNode!=null) {
                soNodeList.add(soNode);
                return new NodeImpl(soNode);
            }
        }
        return null;
    }

    @Override
    public Node removeNode(Map map) {
        if(map!=null){
            SoNode soNode = RenderUtils.removeNode(getNativeSceneGraph(),map);
            if(soNode!=null) {
                soNodeList.add(soNode);
                return new NodeImpl(soNode);
            }
        }
        return null;
    }

    @Override
    public Node removeNode(Layer layer) {
        if(layer!=null){
            SoNode soNode = RenderUtils.removeNode(getNativeSceneGraph(),layer);
            if(soNode!=null) {
                soNodeList.add(soNode);
                return new NodeImpl(soNode);
            }
        }
        return null;
    }

    @Override
    public Node createProjectNode(Project project) {
        if(project!=null){
            try {
                SoNode soNode = RenderUtils.createProjectNode(project);
                if (soNode != null) {
                    Node node =  new NodeImpl(soNode);
                    node.setUserData(project);
                    return node;
                }
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public Node createMapNode(Map map) {
        if(map!=null){
            try {
                SoNode soNode = RenderUtils.createMapNode(map);
                if (soNode != null) {
                    Node node= new NodeImpl(soNode);
                    node.setUserData(map);
                    return node;
                }
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public Node createLayerNode(Layer layer) {
        if(layer!=null){
            try {
                SoNode soNode = RenderUtils.createLayerNode(layer);
                if (soNode != null) {
                    Node node= new NodeImpl(soNode);
                    node.setUserData(layer);
                    return node;
                }
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public Node createGeometryNode(Geometry geometry)  {
        if(geometry!=null){
            try{
                SoNode soNode = RenderUtils.createGeometryNode(geometry);
                if (soNode != null) {
                    Node node = new NodeImpl(soNode);
                    node.setUserData(geometry);
                    return node;
                }
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 显示和隐藏工程结点
     * @param project
     * @param visible
     */
    private void setProjectNodeVisible(SoGroup project, boolean visible){
        if(project!=null){
            for(int i=1;i<project.getNumChildren();++i) {
                setMapNodeVisible((SoGroup) project.getChild(i),visible);
            }
        }
    }

    /**
     * 显示和隐藏图幅结点
     * @param map
     * @param visible
     */
    private void setMapNodeVisible(SoGroup map, boolean visible){
        if(map!=null){
            for(int i=1;i<map.getNumChildren();++i) {
                setLayerNodeVisible((SoGroup) map.getChild(i),visible);
            }
        }
    }
    /**
     * 显示和隐藏图层结点
     * @param layer
     * @param visible
     */
    private void setLayerNodeVisible(SoGroup layer, boolean visible){
        if(layer!=null){
            for(int i=1;i<layer.getNumChildren();++i) {
                if (visible) {
                    SoGroup g = (SoGroup) layer.getChild(i);
                    g.replaceChild(0, new SoDrawStyle());
                }
                else {
                    SoGroup g = (SoGroup) layer.getChild(i);
                    SoDrawStyle soDrawStyle = new SoDrawStyle();
                    soDrawStyle.style.setValue(SoDrawStyle.Styles.INVISIBLE);
                    g.replaceChild(0, soDrawStyle);
                }
            }
        }
    }


    private SoNode getNativeSceneGraph() {
        return sceneGraph;
    }

    private void setNativeSceneGraph(SoNode sceneGraph) {
        this.sceneGraph = sceneGraph;
        this.viewer.setSceneGraph(sceneGraph);
    }


    private ViewerExaminer getNativeViewer() {
        return viewer;
    }


    private void setNativeViewer(ViewerExaminer viewer) {
        this.viewer = viewer;
    }



}
